#include "app_message.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "json/cJSON.h"
#include "log/log.h"

/** 将二进制转化为字符串 */
static char *app_message_hex2str(void *hex_ptr, size_t hex_len)
{
    char *str = (char *)malloc(hex_len * 2 + 1);
    if (!str)
    {
        return NULL;
    }
    unsigned char *temp_ptr = hex_ptr;
    for (size_t i = 0; i < hex_len; i++)
    {
        sprintf(str + i * 2, "%02x", temp_ptr[i]);
    }
    str[hex_len * 2] = '\0';
    return str;
}

static unsigned char app_message_hexChar2hex(char hex_char)
{
    if (hex_char >= '0' && hex_char <= '9')
    {
        return hex_char - '0';
    }
    else if (hex_char >= 'a' && hex_char <= 'f')
    {
        return hex_char - 'a' + 10;
    }
    else if (hex_char >= 'A' && hex_char <= 'F')
    {
        return hex_char - 'A' + 10;
    }
    else
    {
        log_error("Invalid hex char");
        return 0;
    }
}

/** 将字符串转化为二进制  */
static void app_message_str2hex(char *str, void *hex_ptr, size_t hex_len)
{
    unsigned char *temp_ptr = hex_ptr;
    for (size_t i = 0; i < hex_len; i++)
    {
        temp_ptr[i] = 0;
        // 先处理str + 2*i, 这一位储存的是temp_ptr[i]的高位
        temp_ptr[i] += app_message_hexChar2hex(str[2 * i]);
        temp_ptr[i] <<= 4;
        // 再处理str + 2*i +1，这一位储存的是temp_ptr[i]的低位
        temp_ptr[i] += app_message_hexChar2hex(str[2 * i + 1]);
    }
}

/** 消息是由二进制组成，帧格式为：
 *   1. 消息类型，1字节
 *   2. 消息ID的长度，1字节
 *   3. 消息内容的长度，1字节
 *   4. 消息ID，二进制，长度可变
 *   5. 消息内容，二进制，长度可变
 */
Message *app_message_initFromBinary(void *data, size_t len)
{
    // 1. 确保消息的长度大于3字节
    if (len <= 3)
    {
        log_warn("message length too short");
        return NULL;
    }

    // 2. 申请message的内存
    Message *msg = (Message *)malloc(sizeof(Message));
    if (!msg)
    {
        log_error("Not enough memory");
        goto MSG_ALLOC_FAIL;
    }
    memset(msg, 0, sizeof(Message));
    // 3. 读取类型，id长度和内容长度
    memcpy(&msg->type, data, 1);
    memcpy(&msg->id_len, data + 1, 1);
    memcpy(&msg->len, data + 2, 1);
    // 4. 读取id和内容
    msg->id = malloc(msg->id_len);
    if (!msg->id)
    {
        log_error("Not enough memory");
        goto ID_ALLOC_FAIL;
    }
    msg->data = malloc(msg->len);
    if (!msg->data)
    {
        log_error("Not enough memory");
        goto DATA_ALLOC_FAIL;
    }
    memcpy(msg->id, data + 3, msg->id_len);
    memcpy(msg->data, data + 3 + msg->id_len, msg->len);

    log_debug("Messgae init from binary success");
    return msg;

DATA_ALLOC_FAIL:
    free(msg->id);
ID_ALLOC_FAIL:
    free(msg);
MSG_ALLOC_FAIL:
    return NULL;
}

Message *app_message_initFromJson(char *json_str, size_t len)
{
    // 1. 解析Json字符串
    cJSON *root = cJSON_ParseWithLength(json_str, len);
    if (!root)
    {
        log_warn("Json parse failed");
        goto JSON_PARSE_FAIL;
    }

    // 2. 申请message内存
    Message *msg = (Message *)malloc(sizeof(Message));
    if (!msg)
    {
        log_error("Not enough memory");
        goto MSG_ALLOC_FAIL;
    }
    memset(msg, 0, sizeof(Message));

    // 3. 读取类型
    cJSON *connection_type_obj = cJSON_GetObjectItem(root, "connection_type");
    if (!cJSON_IsNumber(connection_type_obj))
    {
        log_warn("Connection type is not number");
        goto JSON_NOT_VALID;
    }

    msg->type = connection_type_obj->valueint;

    // 4. 将id和msg的二进制字符串转化为二进制储存
    cJSON *id_obj = cJSON_GetObjectItem(root, "id");
    if (!cJSON_IsString(id_obj))
    {
        log_warn("Id is not string");
        goto JSON_NOT_VALID;
    }
    cJSON *msg_obj = cJSON_GetObjectItem(root, "msg");
    if (!cJSON_IsString(msg_obj))
    {
        log_warn("Msg is not string");
        goto JSON_NOT_VALID;
    }

    msg->id_len = strlen(id_obj->valuestring) / 2;
    msg->len = strlen(msg_obj->valuestring) / 2;

    msg->id = malloc(msg->id_len);
    if (!msg->id)
    {
        log_error("Not enough memory");
        goto ID_ALLOC_FAIL;
    }
    msg->data = malloc(msg->len);
    if (!msg->data)
    {
        log_error("Not enough memory");
        goto DATA_ALLOC_FAIL;
    }

    app_message_str2hex(id_obj->valuestring, msg->id, msg->id_len);
    app_message_str2hex(msg_obj->valuestring, msg->data, msg->len);

    free(root);
    log_info("Init message from json success");
    return msg;
DATA_ALLOC_FAIL:
    free(msg->id);
ID_ALLOC_FAIL:
JSON_NOT_VALID:
    free(msg);
MSG_ALLOC_FAIL:
    cJSON_Delete(root);
JSON_PARSE_FAIL:
    return NULL;
}

int app_message_saveAsJson(Message *msg, char *json_str, size_t len)
{
    // 此方法会把消息转化为Json格式：我们会将消息的ID和内容转化为字符串表示的二进制
    if (!msg)
    {
        log_warn("message is null");
        return -1;
    }

    if (!json_str)
    {
        log_warn("json_str is null");
        return -1;
    }

    // 使用cJson生成Json字符串
    cJSON *root = cJSON_CreateObject();
    if (!root)
    {
        log_error("cJSON_CreateObject failed");
        goto CJSON_NEW_FAIL;
    }

    cJSON_AddNumberToObject(root, "connection_type", msg->type);
    // 将msg中原始的二进制转化为二进制字符串，放入Json中
    char *id_str = app_message_hex2str(msg->id, msg->id_len);
    if (!id_str)
    {
        log_error("Id str Not enough memory");
        goto ID_STR_NEW_FAIL;
    }

    char *data_str = app_message_hex2str(msg->data, msg->len);
    if (!data_str)
    {
        log_error("Data str Not enough memory");
        goto DATA_STR_NEW_FAIL;
    }
    cJSON_AddStringToObject(root, "id", id_str);
    cJSON_AddStringToObject(root, "msg", data_str);
    char *temp_str = cJSON_PrintUnformatted(root);
    if (strlen(temp_str)+ 1 >= len)
    {
        log_error("Out buffer not enough");
        goto OUT_BUFFER_NOT_ENOUGH;
    }
    snprintf(json_str, len, "%s", temp_str);
    free(temp_str);
    free(data_str);
    free(id_str);
    // 使用完cJSON后，要释放
    cJSON_Delete(root);

    log_info("Save message to json success");
    return strlen(json_str);
OUT_BUFFER_NOT_ENOUGH:
    free(data_str);
DATA_STR_NEW_FAIL:
    free(id_str);
ID_STR_NEW_FAIL:
    cJSON_Delete(root);
CJSON_NEW_FAIL:
    return -1;
}

/** 消息是由二进制组成，帧格式为：
 *   1. 消息类型，1字节
 *   2. 消息ID的长度，1字节
 *   3. 消息内容的长度，1字节
 *   4. 消息ID，二进制，长度可变
 *   5. 消息内容，二进制，长度可变
 */
int app_message_saveAsBinary(Message *msg, void *data, size_t len)
{
    // 1. 首先校验输出的长度
    if (len < 3 + msg->id_len + msg->len)
    {
        log_warn("Out buffer not enough");
        return -1;
    }
    // 2. 写入类型，id长度和内容长度
    memcpy(data, &msg->type, 1);
    memcpy(data + 1, &msg->id_len, 1);
    memcpy(data + 2, &msg->len, 1);
    // 3. 写入id和内容
    memcpy(data + 3, msg->id, msg->id_len);
    memcpy(data + 3 + msg->id_len, msg->data, msg->len);
    log_info("Save message to binary success");
    return 3 + msg->id_len + msg->len;
}

void app_message_free(Message *msg)
{
    free(msg->id);
    free(msg->data);
    free(msg);
}
